package handler.impl;

import config.ServerConfig;
import database.impl.MulIDB;
import database.inter.IDB;
import exception.CustomException;
import handler.inter.Handler;
import jodis.conn.impl.EmptyMultiBulkReply;
import jodis.conn.impl.JoConnection;
import jodis.conn.inter.IReply;
import parser.Parser;
import parser.ReadState;
import rdb.RDBParser;
import rdb.model.BaseObject;
import tcp.Server;
import tcp.utils.StreamUtils;

import java.io.*;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

public class ServerHandler implements Handler {

    private ConcurrentHashMap activeConn;
    private IDB db;
    private Logger logger;
    private BufferedOutputStream out;

    public ServerHandler() {
        activeConn = new ConcurrentHashMap();
        MulIDB dbImpl = new MulIDB();

        // 先获取配置
        ServerConfig config = Server.getServerConfig();

        // 如果开启 aof，则尝试读取 aof 文件中的内容
        dbImpl.loadAof();
        db = dbImpl;

        try {
            out = new BufferedOutputStream(new FileOutputStream("redis_benchmark.bin"));
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 如果没有开启 aof，则尝试读取 rdb 文件
        if (!config.appendOnly && new File(config.rdbFileName).exists()) {
            RDBParser parser = new RDBParser(config.rdbFileName);
            System.out.println("read rdb");
            List<BaseObject> dbObjs = parser.parse();
            if (dbObjs != null) {
                JoConnection connection = JoConnection.fakeConn(0);
                for (BaseObject baseObject : dbObjs) {
                    connection.selectDB(baseObject.getDBIndex());
                    db.exec(connection, baseObject.getCmdLines());
                    if (baseObject.getExpiration() != 0) {
                        db.exec(connection, new String[]{"pexpireat", baseObject.getKey(), String.valueOf(baseObject.getExpiration())});
                    }
                }
            }
        }

        // 如果开启 aof，则打开 aof 写入线程
        if (config.appendOnly) {
            dbImpl.handleAof();
        }

        logger = Logger.getLogger("ServerHandler");
    }

//    public void handleWithNIO(byte[] bs, SocketChannel client) throws IOException {
//        ReadState state = ReadState.getDefaultInstance();
//        Parser.parse(bs, state);
//        JoConnection clientConn = new JoConnection(client);
//        if (state.msgType == Parser.ARRAY) {
//            if (state.resultArr[0].equalsIgnoreCase("command")) {
//                clientConn.writeWithNIO(EmptyMultiBulkReply.makeEmptyMultiBulkReply().toBytes());
//                return;
//            }
//
//            // 交给对应的 db 去处理
//            IReply reply = db.exec(clientConn, state.resultArr);
//            clientConn.writeWithNIO(reply.toBytes());
//        } else if (state.msgType != Parser.NONE) {
//            logger.info(state.resultStr);
//        } else {
//            logger.info(String.valueOf(state.msgType));
//        }
//    }

    @Override
    public void handle(Socket conn) {
        // 根据 socket 连接，获取客户端发来的数据
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
        ReadState state = ReadState.getDefaultInstance();
        byte[] bs;

        JoConnection clientConn = new JoConnection(conn);

        while (true) {
            try {
                bs = StreamUtils.readStream(byteStream, conn.getInputStream(), StreamUtils.readWait(), state);
                out.write(bs);
                out.flush();
                Parser.parse(bs, state);
                if (state.msgType == Parser.ARRAY) {
                    // 交给对应的 db 去处理
                    for (String[] cmd : state.cmds) {
                        IReply reply = db.exec(clientConn, cmd);
                        clientConn.write(reply.toBytes());
                    }
                } else if (state.msgType != Parser.NONE) {
                    logger.info(state.resultStr);
                } else {
                    logger.info(String.valueOf(state.msgType));
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (CustomException ignore) {}
        }
    }

    @Override
    public void close() {

    }
}
